package com.yunhe.abnormal.service.data.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.yunhe.abnormal.client.AuthorityClient;
import com.yunhe.common.constant.AbnormalConsts;
import com.yunhe.common.constant.CacheConsts;
import com.yunhe.common.constant.SystemConsts;
import com.yunhe.abnormal.domain.data.AbnormalEvent;
import com.yunhe.abnormal.domain.data.AbnormalEventRecord;
import com.yunhe.abnormal.domain.data.AlarmEvent;
import com.yunhe.common.exception.ObjectNotFoundException;
import com.yunhe.common.model.PageParam;
import com.yunhe.common.model.authority.StationDTO;
import com.yunhe.common.model.authority.UserDTO;
import com.yunhe.common.model.base.AbnormalStatusDTO;
import com.yunhe.common.model.base.AlarmLevelDTO;
import com.yunhe.abnormal.repository.data.AbnormalEventRecordRepository;
import com.yunhe.abnormal.repository.data.AbnormalEventRepository;
import com.yunhe.abnormal.service.base.AbnormalStatusService;
import com.yunhe.abnormal.service.data.AbnormalEventRecordService;
import com.yunhe.abnormal.service.data.AbnormalEventService;
import com.yunhe.common.util.DateTimeUtils;
import com.yunhe.common.util.HeaderUtil;
import com.yunhe.common.util.QueryUtil;
import net.oschina.j2cache.cache.support.util.SpringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.util.*;

@Service
public class AbnormalEventServiceImpl implements AbnormalEventService {
    @Autowired
    private AbnormalEventRepository abnormalEventRepository;
    @Autowired
    private AbnormalEventRecordRepository abnormalEventRecordRepository;
//    @Autowired
//    private AbnormalRuleService abnormalRuleService;
//    @Autowired
//    private AlarmLevelService alarmLevelService;
    @Autowired
    private AbnormalStatusService abnormalStatusService;
    @Autowired
    private AbnormalEventRecordService abnormalEventRecordService;
    @Autowired
    private AuthorityClient authorityClient;
    @Autowired
    private HeaderUtil headerUtil;
//    @Autowired
//    private KafkaTemplate<String, String> kafkaTemplate;

    private static final Logger logger = LoggerFactory.getLogger(AbnormalEventServiceImpl.class);

    private void processBeforeSave(AbnormalEvent abnormalEvent) throws ObjectNotFoundException {
//        Long stationId = abnormalEvent.getStationId();
//        Long abnormalRuleId = abnormalEvent.getAlarmId();
//        StationDTO station = authorityClient.getStationById(stationId).getBody().getResults();
//        if (station != null && station.getTitle() != null) {
//            if (stationId != null && abnormalEvent.getStationTitle() == null) {
//                abnormalEvent.setStationTitle(station.getTitle());
//            }
//        } else if (station == null || station.getTitle() == null) {
//            throw new ObjectNotFoundException("未找到ID为" + stationId + "的电站");
//        }
//        String timeZone = authorityClient.getTimeZoneIdByStationId(abnormalEvent.getStationId()).getBody().getResults();
//        if (timeZone == null) {
//            logger.error("电站{}的时区未查询到", stationId);
//            throw new ObjectNotFoundException("该事件所属电站时区未找到");
//        }
//        Long rtime = System.currentTimeMillis();
//        String dtime = DateTimeUtils.getDateString(rtime, timeZone);
        //设置设备名称栏属性
//        Long deviceId = abnormalEvent.getDevId();
//        if (deviceId != null && abnormalEvent.getDevTitle() == null) {
//            String label = abnormalRuleService.getLabel(deviceId);
//            if ("Device".equals(label)) {
//                String deviceTitle = deviceClient.getDevicePathTitleByDeviceId(deviceId).getBody().getResults();
//                if (deviceTitle != null && deviceTitle != "") {
//                    abnormalEvent.setDevTitle(deviceTitle);
//                } else {
//                    logger.error("未找到ID为{}的设备", deviceId);
//                }
//            } else if ("EnergyUnit".equals(label)) {
//                EnergyUnitDTO energyUnit = deviceClient.getEnergyUnitById(deviceId).getBody().getResults();
//                if (energyUnit != null) {
//                    abnormalEvent.setDevTitle(energyUnit.getTitle());
//                } else {
//                    logger.error("未找到ID为{}的能量单元", deviceId);
//                }
//            } else if ("Station".equals(label)) {
//                abnormalEvent.setDevTitle(station.getTitle());
//            }
//        }
//        if (abnormalRuleId != null && abnormalEvent.getAlarmTitle() == null) {
//            AbnormalRule abnormalRule = abnormalRuleService.findById(abnormalRuleId);
//            if (abnormalRule != null && abnormalRule.getTitle() != null) {
//                abnormalEvent.setAlarmTitle(abnormalRule.getTitle());
//            } else {
//                throw new ObjectNotFoundException("未找到ID为" + abnormalRuleId + "的异常规则");
//            }
//        }
//        abnormalEvent.setDevTitle(abnormalRuleService.getDeviceTitleByRuleId(abnormalRuleId));
//        abnormalEvent.setAlarmTitle(abnormalRuleService.getRuleTitleByRuleId(abnormalRuleId));
        //header中language影响处理状态枚举返回Title
//        Long abnormalStatusId = abnormalEvent.getAbnormalStatusId();
//        AbnormalStatusDTO abnormalStatus;
//        if (abnormalStatusId != null) {
//            abnormalStatus = abnormalStatusService.findById(abnormalStatusId);
//        } else {
//            abnormalStatus = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.READY.getName());
//        }
//        if (abnormalStatus != null && abnormalStatus.getTitle() != null) {
//            abnormalEvent.setAbnormalStatusId(abnormalStatus.getId());
//            abnormalEvent.setAbnormalStatusTitle(abnormalStatus.getTitle());
//        } else {
//            throw new ObjectNotFoundException("未找到ID为" + abnormalStatusId + "的异常处理状态");
//        }
        //若为已消除，则更新处理时间和持续时间
//        if (abnormalStatus.getName().equals(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName()) && abnormalEvent.getLatestProcessRtime() == null) {
//            abnormalEvent.setLatestProcessTime(dtime);
//            abnormalEvent.setLatestProcessRtime(rtime);
//        }
        //header中language影响告警等级枚举返回Title
//        Long alarmLevelId = abnormalEvent.getAlarmLevelId();
//        if (alarmLevelId != null && abnormalEvent.getAlarmLevelTitle() == null) {
//            AlarmLevelDTO alarmLevel = alarmLevelService.findById(alarmLevelId);
//            if (alarmLevel != null && alarmLevel.getTitle() != null) {
//                abnormalEvent.setAlarmLevelTitle(alarmLevel.getTitle());
//            } else {
//                throw new ObjectNotFoundException("未找到ID为" + alarmLevelId + "的告警等级");
//            }
//        }
//        String startTime = abnormalEvent.getStartTime();
//        if (startTime == null) {
//            abnormalEvent.setStartTime(dtime);
//            abnormalEvent.setStartRtime(rtime);
//        }
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AbnormalEvent create(AbnormalEvent abnormalEvent) throws ObjectNotFoundException, JsonProcessingException {
        //判断是否有同规则未消除的事件
//        AbnormalStatusDTO eliminated = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName());
//        if (eliminated == null) {
//            logger.warn("未找到待处理或已消除的告警处理状态");
//            return null;
//        }
//        else {
//            //如果未消除的事件等级跟要新增的相同，则不新增；若等级不同，则更新等级
//            Long alarmLevelId = abnormalEvent.getAlarmLevelId();
//            if (CollectionUtil.isNotEmpty(relatedEvents)) {
//                relatedEvents.stream().forEach(relatedEvent -> {
//                    AbnormalEvent updateEvent = MAPPER.convertValue(relatedEvent, AbnormalEvent.class);
//                    if (updateEvent != null && !alarmLevelId.equals(updateEvent.getAlarmLevelId())) {
//                        updateEvent.setAlarmLevelId(alarmLevelId);
//                        try {
//                            update(updateEvent);
//                        } catch (ObjectNotFoundException e) {
//                            e.printStackTrace();
//                        }
//                    }
//                });
//                return null;
//            }
//        }
        return createEvent(abnormalEvent);
    }

    /**
     * 新增事件对象
     * @param abnormalEvent 需要新增的事件对象
     */
    private AbnormalEvent createEvent(AbnormalEvent abnormalEvent) throws ObjectNotFoundException, JsonProcessingException {
        abnormalEvent.setId(null);

        processBeforeSave(abnormalEvent);
        AbnormalEvent result = abnormalEventRepository.save(abnormalEvent);
        //新建事件记录
        createRecord(result, SystemConsts.ACTION.ADD, null);
        return result;
    }

    /**
     * 新建历史告警，只需填充字段，不需要其他处理
     * @param abnormalEvent
     * @return
     * @throws ObjectNotFoundException
     * @throws JsonProcessingException
     */
    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AbnormalEvent createForHistory(AbnormalEvent abnormalEvent) throws ObjectNotFoundException {
        //查找相同规则相同开始时间的记录，若已经生成过，则不生成新的
        AbnormalEvent sameOne = abnormalEventRepository.findSameRuleAndRtime(abnormalEvent.getAlarmId(), abnormalEvent.getStartRtime());
        if (sameOne != null) {
            return null;
        }
        processBeforeSave(abnormalEvent);
        return abnormalEventRepository.save(abnormalEvent);
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public Iterable<AbnormalEvent> create(List<AbnormalEvent> abnormalEvents) throws ObjectNotFoundException, JsonProcessingException {
        List<AbnormalEvent> results = new ArrayList<>();
        for (AbnormalEvent abnormalEvent : abnormalEvents) {
            results.add(createEvent(abnormalEvent));
        }
        return results;
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public void delete(Long id) {
        abnormalEventRecordRepository.deleteByEventId(id);
        abnormalEventRepository.deleteById(id);
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AbnormalEvent update(AbnormalEvent abnormalEvent) throws ObjectNotFoundException {
//        processBeforeSave(abnormalEvent);
        AbnormalEvent result = abnormalEventRepository.save(abnormalEvent);
        Long currentUserId = headerUtil.getHeaderUserId();
        if (currentUserId != null) {
            createRecord(result, SystemConsts.ACTION.STATUS_UPDATE, currentUserId);
        } else {
            createRecord(result, SystemConsts.ACTION.STATUS_UPDATE, null);
        }
        //还需要实现旧表数据的消除
        return result;
    }

    @Override
    @CacheEvict(value = CacheConsts.Region.ALARM_EVENT_PENDING, allEntries = true)
    public AbnormalEvent update(Long id,
                                Optional<Long> stationId,
                                Optional<Long> deviceId,
                                Optional<Long> abnormalRuleId,
                                Optional<Long> userIdProcess,
                                Optional<Long> abnormalStatusId,
                                Optional<Long> alarmLevelId,
                                Optional<String> latestProcessTime) throws ObjectNotFoundException {
        AbnormalEvent byId = abnormalEventRepository.findById(id).orElse(null);
        if (byId == null) {
            throw new ObjectNotFoundException("找不到ID为" + id + "的事件");
        }
        stationId.ifPresent(byId::setStationId);
        deviceId.ifPresent(byId::setDevId);
        abnormalRuleId.ifPresent(byId::setAlarmId);
        abnormalStatusId.ifPresent(byId::setAbnormalStatusId);
        latestProcessTime.ifPresent(byId::setLatestProcessTime);
        processBeforeSave(byId);
        AbnormalEvent result = abnormalEventRepository.save(byId);
        if (userIdProcess.isPresent()) {
            createRecord(result, SystemConsts.ACTION.EDIT, userIdProcess.get());
        }
        //还需要实现旧表数据的消除
        return result;
    }

    @Override
    public AbnormalEvent findById(Long id) {
        return abnormalEventRepository.findById(id).orElse(null);
    }

    @Override
    public Iterable<AbnormalEvent> retrieve(Optional<List<Long>> stationIds,
                                            Optional<List<Long>> devId,
                                            Optional<Long> abnormalRuleId,
                                            Optional<List<Long>> abnormalStatusIds,
                                            Optional<List<Long>> alarmLevelIds,
                                            Optional<String> dtime,
                                            Optional<String> queryStr,
                                            PageParam pageParam) {
        Specification spec = (Specification<AbnormalEvent>) (root, query, cb) -> {
            List<Predicate> criteria = new ArrayList<>();
            if (stationIds.isPresent()) {
                CriteriaBuilder.In<Long> in = cb.in(root.get("stationId"));
                for (Long stationId : stationIds.get()) {
                    in.value(stationId);
                }
                criteria.add(in);
            }
            if (devId.isPresent()) {
                CriteriaBuilder.In<Long> in = cb.in(root.get("devId"));
                for (Long deviceId : devId.get()) {
                    in.value(deviceId);
                }
                criteria.add(in);
            }
            if (abnormalRuleId.isPresent()) {
                criteria.add(cb.equal(root.get("abnormalRuleId"), abnormalRuleId.get()));
            }
            if (abnormalStatusIds.isPresent()) {
                CriteriaBuilder.In<Long> in = cb.in(root.get("abnormalStatusId"));
                for (Long abnormalStatus : abnormalStatusIds.get()) {
                    in.value(abnormalStatus);
                }
                criteria.add(in);
            }
            if (alarmLevelIds.isPresent()) {
                CriteriaBuilder.In<Long> in = cb.in(root.get("alarmLevelId"));
                for (Long alarmLevel : alarmLevelIds.get()) {
                    in.value(alarmLevel);
                }
                criteria.add(in);
            }
            if (dtime.isPresent()) {
                String[] split = dtime.get().split(",");
                List<String> dtimeList = new ArrayList<>();
                Collections.addAll(dtimeList, split);
                Path<String> dtimePath = root.get("startTime");
                if (dtimeList.size() == 1) {
                    String date = dtimeList.get(0);
                    criteria.add(cb.equal(dtimePath, date));
                } else if (dtimeList.size() == 2) {
                    String start = dtimeList.get(0);
                    String end = dtimeList.get(1);
                    criteria.add(cb.between(dtimePath, start, end));
                }
            }
            if (queryStr.isPresent()) {
                Predicate p = cb.or(cb.like(root.get("devTitle"), "%" + queryStr.get().replace("%", "\\%") + "%"),
                        cb.like(root.get("abnormalRuleTitle"), "%" + queryStr.get().replace("%", "\\%") + "%"),
                        cb.like(root.get("records"), "%" + queryStr.get().replace("%", "\\%") + "%"));
                criteria.add(p);
            }
            if (criteria.size() == 1) {
                query.where(criteria.get(0));
            } else {
                query.where(criteria.toArray(new Predicate[0]));
            }
            return null;
        };
        Iterable<AbnormalEvent> results;
        if (!stationIds.isPresent()) {
            return null;
        }
        if (QueryUtil.needPaging(pageParam)) {
            List<String> sortList = pageParam.getSort();
            if (sortList == null) {
                List<String> list = new ArrayList<>();
                list.add("-startTime");
                pageParam.setSort(list);
            }
            results = abnormalEventRepository.findAll(spec, QueryUtil.getPageRequest(pageParam));
        } else {
            Sort sort = pageParam == null ? null : QueryUtil.getSort(pageParam.getSort());
            if (sort != null) {
                results = abnormalEventRepository.findAll(spec, sort);
            } else {
                List<String> list = new ArrayList<>();
                list.add("-startTime");
                sort = QueryUtil.getSort(list);
                results = abnormalEventRepository.findAll(spec, sort);
            }
        }
        return results;
    }

    @Override
    public List<AbnormalEvent> findByAbnormalRuleIdAndAlarmLevelIdAndStatusId(Long abnormalRuleId, Long alarmLevelId, Long abnormalStatusId) {
        Map<String, List<AbnormalEvent>> readyEventMap = SpringUtil.getBean(AbnormalEventService.class).getHistoryEventMap();
        String key = generateKey(abnormalRuleId, alarmLevelId);
        return readyEventMap.get(key);
    }

    @Override
    public void clearAbnormalEventByAlarmEventId(AlarmEvent alarmEvent) throws ObjectNotFoundException {
        if (alarmEvent.getAbnormalStatusName().equals(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName())) {
            List<AbnormalEvent> abnormalEvents = findByAbnormalRuleIdAndAlarmLevelIdAndStatusId(alarmEvent.getAlarmId(), alarmEvent.getAlarmLevelId(),
                    alarmEvent.getAbnormalStatusId());
            if (abnormalEvents !=null && !abnormalEvents.isEmpty()) {
                abnormalEvents.stream().forEach(abnormalEvent -> {
                    try {
                        abnormalEvent.setAbnormalStatusId(alarmEvent.getAbnormalStatusId());
                        abnormalEvent.setAbnormalStatusTitle(alarmEvent.getAbnormalStatusTitle());
                        abnormalEvent.setLatestProcessTime(alarmEvent.getLatestProcessTime());
                        abnormalEvent.setLatestProcessRtime(alarmEvent.getLatestProcessRtime());
                        SpringUtil.getBean(AbnormalEventService.class).update(abnormalEvent);
                    } catch (ObjectNotFoundException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }

    private void createRecord(AbnormalEvent abnormalEvent, SystemConsts.ACTION action, Long userId) throws ObjectNotFoundException {
        AbnormalEventRecord abnormalEventRecord = new AbnormalEventRecord();
        abnormalEventRecord.setAbnormalEvent(abnormalEvent);
        String processTime = abnormalEvent.getLatestProcessTime();
        if (processTime == null) {
            abnormalEventRecord.setDtime(abnormalEvent.getStartTime());
        } else {
            abnormalEventRecord.setDtime(DateTimeUtils.format(new Date()));
        }
        abnormalEventRecord.setAction(action.title());
        if (SystemConsts.ACTION.ADD.equals(action)) {
            abnormalEventRecord.setRecords(SystemConsts.ACTION.ADD.title());
        } else if (SystemConsts.ACTION.STATUS_UPDATE.equals(action)) {
            abnormalEventRecord.setRecords(abnormalEvent.getAbnormalStatusTitle());
        } else if (SystemConsts.ACTION.EDIT.equals(action)) {
            abnormalEventRecord.setRecords(abnormalEvent.getAbnormalStatusTitle());
        }
        if (userId != null) {
            UserDTO user = authorityClient.getUserById(userId).getBody().getResults();
            if (user != null && user.getTitle() != null) {
                abnormalEventRecord.setUserIdProcess(userId);
                abnormalEventRecord.setUserTitleProcess(user.getTitle());
            }
        } else {
            abnormalEventRecord.setUserTitleProcess("System");
        }
        abnormalEventRecordService.create(abnormalEventRecord);
    }

    /**
     * 获得待处理的告警事件Map
     */
    @Override
    @Cacheable(value = CacheConsts.Region.ALARM_EVENT_PENDING)
    public Map<String, List<AbnormalEvent>> getHistoryEventMap() {
        AbnormalStatusDTO ready = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.READY.getName());
        List<AbnormalEvent> abnormalEvents = abnormalEventRepository.findAllReadyEvents(ready.getId());
        Map<String, List<AbnormalEvent>> map = new HashMap<>();
        for (AbnormalEvent abnormalEvent : abnormalEvents) {
            String key = generateKey(abnormalEvent.getAlarmId(), abnormalEvent.getAlarmLevelId());
            List<AbnormalEvent> relatedEvents = map.computeIfAbsent(key, k -> new ArrayList<>());
            relatedEvents.add(abnormalEvent);
        }
        return map;
    }

    @Override
    public String generateKey(Long abnormalRuleId, Long alarmLevelId) {
        return abnormalRuleId + "_" + alarmLevelId;
    }
}
